﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Aribaccio
{
    /// <summary>
    /// 定义扩展结构数据转换
    /// </summary>
    public static class ExtendStructSerialize
    {
        /// <summary>
        /// 结构体转大小（以字节为单位）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="argStructureItem"></param>
        /// <returns></returns>
        public static int StructToSizeOf<T>(this T argStructureItem) where T : struct
        {
            return Marshal.SizeOf(argStructureItem);
        }
        /// <summary>
        /// 结构体转字节数组
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argStructureItem">结构体实例</param>
        /// <returns>字节数组</returns>
        public static Byte[] StructToBytes<T>(this T argStructureItem) where T : struct
        {
            Int32 size = Marshal.SizeOf(argStructureItem);
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr(argStructureItem, buffer, false);
                Byte[] bytes = new Byte[size];
                Marshal.Copy(buffer, bytes, 0, size);
                Array.Reverse(bytes);
                return bytes;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }
        /// <summary>
        /// 结构体转字节数组[按大端模式][定义为属性时候使用]
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argStructureItem">结构体实例</param>
        /// <returns>字节数组</returns>
        public static Byte[] StructToBytesEndian<T>(this T argStructureItem) where T : struct
        {
            byte[] data = new byte[0];
            Type StructType = argStructureItem.GetType();
            //获取结构体的属性集合[定义为属性时候使用]
            PropertyInfo[] PropertyInfoLitems = StructType.GetProperties();
            //获取结构体的字段集合[定义为字段时候使用]
            //FieldInfo[] FieldInfoLitems = StructType.GetFields();

            //定义临时变量
            object? propertyValue;   //定义存储属性/字段数据值
            TypeCode typeCode;      //定义存储属性/字段数据类型
            int offset = 0;         //定义存储属性/字段数据起始位置
            byte[] temp;            //定义存储属性/字段数据转换字节数组
            if (PropertyInfoLitems != null && PropertyInfoLitems.Length > 0)
            {
                data = new byte[Marshal.SizeOf(argStructureItem)];
                // 列举结构体的每个成员，并Reverse
                foreach (PropertyInfo propertyInfo in PropertyInfoLitems)
                {
                    propertyValue = propertyInfo.GetValue(argStructureItem);
                    if (propertyValue == null) continue;
                    typeCode = Type.GetTypeCode(propertyValue.GetType());
                    switch (typeCode)
                    {
                        case TypeCode.Single: // float
                            {
                                temp = BitConverter.GetBytes((Single)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Single));
                                break;
                            }
                        case TypeCode.Int32:
                            {
                                temp = BitConverter.GetBytes((Int32)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int32));
                                break;
                            }
                        case TypeCode.UInt32:
                            {
                                temp = BitConverter.GetBytes((UInt32)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt32));
                                break;
                            }
                        case TypeCode.Int16:
                            {
                                temp = BitConverter.GetBytes((Int16)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int16));
                                break;
                            }
                        case TypeCode.UInt16:
                            {
                                temp = BitConverter.GetBytes((UInt16)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt16));
                                break;
                            }
                        case TypeCode.Int64:
                            {
                                temp = BitConverter.GetBytes((Int64)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int64));
                                break;
                            }
                        case TypeCode.UInt64:
                            {
                                temp = BitConverter.GetBytes((UInt64)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt64));
                                break;
                            }
                        case TypeCode.Double:
                            {
                                temp = BitConverter.GetBytes((Double)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Double));
                                break;
                            }
                        case TypeCode.Byte:
                            {
                                data[offset] = (Byte)propertyValue;
                                break;
                            }
                    }
                    ;
                    if (typeCode == TypeCode.Object)
                    {
                        int length = 0;
                        try
                        {
                            length = ((byte[])propertyValue).Length;
                        }
                        catch (Exception)
                        {
                            //异常表示引用类型
                        }
                        if (length > 0)
                        {
                            Array.Copy(((byte[])propertyValue), 0, data, offset, length);
                            offset += length;
                        }
                    }
                    else
                    {
                        offset += Marshal.SizeOf(propertyValue);
                    }
                }
            }
            return data;
        }
        /// <summary>
        /// 结构体转字节数组[按大端模式][定义为字段时候使用]
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argStructureItem">结构体实例</param>
        /// <returns>字节数组</returns>
        public static Byte[] StructToBytesEndianFields<T>(this T argStructureItem) where T : struct
        {
            byte[] data = new byte[0];
            Type StructType = argStructureItem.GetType();
            //获取结构体的属性集合定义为字段时候使用
            //PropertyInfo[] PropertyInfoLitems = StructType.GetProperties();
            //获取结构体的字段集合[定义为字段时候使用]
            FieldInfo[] FieldInfoLitems = StructType.GetFields();

            //定义临时变量
            object? propertyValue;   //定义存储属性/字段数据值
            TypeCode typeCode;      //定义存储属性/字段数据类型
            int offset = 0;         //定义存储属性/字段数据起始位置
            byte[] temp;            //定义存储属性/字段数据转换字节数组
            if (FieldInfoLitems != null && FieldInfoLitems.Length > 0)
            {
                data = new byte[Marshal.SizeOf(argStructureItem)];
                // 列举结构体的每个成员，并Reverse
                foreach (FieldInfo propertyInfo in FieldInfoLitems)
                {
                    propertyValue = propertyInfo.GetValue(argStructureItem);
                    if (propertyValue == null) continue;
                    typeCode = Type.GetTypeCode(propertyValue.GetType());
                    switch (typeCode)
                    {
                        case TypeCode.Single: // float
                            {
                                temp = BitConverter.GetBytes((Single)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Single));
                                break;
                            }
                        case TypeCode.Int32:
                            {
                                temp = BitConverter.GetBytes((Int32)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int32));
                                break;
                            }
                        case TypeCode.UInt32:
                            {
                                temp = BitConverter.GetBytes((UInt32)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt32));
                                break;
                            }
                        case TypeCode.Int16:
                            {
                                temp = BitConverter.GetBytes((Int16)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int16));
                                break;
                            }
                        case TypeCode.UInt16:
                            {
                                temp = BitConverter.GetBytes((UInt16)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt16));
                                break;
                            }
                        case TypeCode.Int64:
                            {
                                temp = BitConverter.GetBytes((Int64)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Int64));
                                break;
                            }
                        case TypeCode.UInt64:
                            {
                                temp = BitConverter.GetBytes((UInt64)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(UInt64));
                                break;
                            }
                        case TypeCode.Double:
                            {
                                temp = BitConverter.GetBytes((Double)propertyValue);
                                Array.Reverse(temp);
                                Array.Copy(temp, 0, data, offset, sizeof(Double));
                                break;
                            }
                        case TypeCode.Byte:
                            {
                                data[offset] = (Byte)propertyValue;
                                break;
                            }
                    }
                    ;
                    if (typeCode == TypeCode.Object)
                    {
                        int length = 0;
                        try
                        {
                            length = ((byte[])propertyValue).Length;
                        }
                        catch (Exception)
                        {
                            //异常表示引用类型
                        }
                        if (length > 0)
                        {
                            Array.Copy(((byte[])propertyValue), 0, data, offset, length);
                            offset += length;
                        }
                    }
                    else
                    {
                        offset += Marshal.SizeOf(propertyValue);
                    }
                }
            }
            return data;
        }
        /// <summary>
        /// 字节数组转结构体
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argByteItems">字节数组</param>
        /// <returns>结构体实例</returns>
        public static T BytesToStruct<T>(this Byte[] argByteItems) where T : struct
        {
            Int32 size = Marshal.SizeOf(typeof(T));
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(argByteItems, 0, buffer, size);
                var data = Marshal.PtrToStructure(buffer, typeof(T));
                return data != null ? (T)data : default(T);
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }
        /// <summary>
        /// 字节数组转结构体[按大端模式][定义为属性时候使用]
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argByteItems">字节数组</param>
        /// <returns>结构体实例</returns>
        public static T BytesToStructEndian<T>(this Byte[] argByteItems) where T : struct
        {
            if (argByteItems.Length <= 0) { return default(T); }
            Int32 size = Marshal.SizeOf(typeof(T));
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                byte[] TempArray = new byte[size];
                if (argByteItems.Length < size)
                {
                    for (int i = 0; i < TempArray.Length; i++)
                    {
                        TempArray[i] = ((i + 1) <= argByteItems.Length) ? argByteItems[i] : (byte)0;
                    }
                }
                else { TempArray = (byte[])argByteItems.Clone(); }
                // 从结构体指针构造结构体
                Object? ObjStrcut = Marshal.PtrToStructure(buffer, typeof(T));
                if (ObjStrcut == null) return default(T);

                Type StructType = ObjStrcut.GetType();
                //获取结构体的属性集合[定义为属性时候使用]
                PropertyInfo[] PropertyInfoLitems = StructType.GetProperties();
                //获取结构体的字段集合[定义为字段时候使用]
                //FieldInfo[] FieldInfoLitems = StructType.GetFields();

                //定义临时变量
                int reversestartoffset = 0; //argBytes内的起始位置
                object? propertyValue;       //定义存储属性/字段数据值
                TypeCode typeCode;      //定义存储属性/字段数据类型
                // 列举结构体的每个成员，并Reverse
                foreach (PropertyInfo propertyInfo in PropertyInfoLitems)
                {
                    propertyValue = propertyInfo.GetValue(ObjStrcut);
                    if (propertyValue == null) continue;
                    typeCode = Type.GetTypeCode(propertyValue.GetType());
                    if (typeCode != TypeCode.Object)  //如果为值类型
                    {
                        if (reversestartoffset < TempArray.Length)
                        {
                            try
                            {
                                Array.Reverse(TempArray, reversestartoffset, Marshal.SizeOf(propertyValue));
                                reversestartoffset += Marshal.SizeOf(propertyValue);
                            }
                            catch (Exception) { }
                        }
                    }
                    else
                    {
                        //如果为引用类型
                        try
                        {
                            reversestartoffset += ((byte[])propertyValue).Length;
                        }
                        catch (Exception)
                        {
                            //已经表示引用类型
                        }
                    }
                }
                //将字节数组复制到结构体指针
                Marshal.Copy(TempArray, 0, buffer, size);
                ObjStrcut = Marshal.PtrToStructure(buffer, typeof(T));
                return ObjStrcut != null ? (T)ObjStrcut : default(T);
            }
            catch (Exception) { return default(T); }
            finally { Marshal.FreeHGlobal(buffer); }
        }
        /// <summary>
        /// 字节数组转结构体[按大端模式][定义为字段时候使用]
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="argByteItems">字节数组</param>
        /// <returns>结构体实例</returns>
        public static T BytesToStructEndianFields<T>(this Byte[] argByteItems) where T : struct
        {
            if (argByteItems.Length <= 0) { return default(T); }
            Int32 size = Marshal.SizeOf(typeof(T));
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                byte[] TempArray = new byte[size];
                if (argByteItems.Length < size)
                {
                    for (int i = 0; i < TempArray.Length; i++)
                    {
                        TempArray[i] = ((i + 1) <= argByteItems.Length) ? argByteItems[i] : (byte)0;
                    }
                }
                else { TempArray = (byte[])argByteItems.Clone(); }
                // 从结构体指针构造结构体
                Object? ObjStrcut = Marshal.PtrToStructure(buffer, typeof(T));
                if (ObjStrcut == null) return default(T);

                Type StructType = ObjStrcut.GetType();
                //获取结构体的属性集合[定义为属性时候使用]
                //PropertyInfo[] PropertyInfoLitems = StructType.GetProperties();
                //获取结构体的字段集合[定义为字段时候使用]
                FieldInfo[] FieldInfoLitems = StructType.GetFields();

                //定义临时变量
                int reversestartoffset = 0; //argBytes内的起始位置
                object? propertyValue;       //定义存储属性/字段数据值
                TypeCode typeCode;      //定义存储属性/字段数据类型
                // 列举结构体的每个成员，并Reverse
                foreach (FieldInfo propertyInfo in FieldInfoLitems)
                {
                    propertyValue = propertyInfo.GetValue(ObjStrcut);
                    if (propertyValue == null) continue;

                    typeCode = Type.GetTypeCode(propertyValue.GetType());
                    if (typeCode != TypeCode.Object)  //如果为值类型
                    {
                        if (reversestartoffset < TempArray.Length)
                        {
                            try
                            {
                                Array.Reverse(TempArray, reversestartoffset, Marshal.SizeOf(propertyValue));
                                reversestartoffset += Marshal.SizeOf(propertyValue);
                            }
                            catch (Exception) { }
                        }
                    }
                    else
                    {
                        //如果为引用类型
                        try
                        {
                            reversestartoffset += ((byte[])propertyValue).Length;
                        }
                        catch (Exception)
                        {
                            //已经表示引用类型
                        }
                    }
                }
                //将字节数组复制到结构体指针
                Marshal.Copy(TempArray, 0, buffer, size);
                ObjStrcut = Marshal.PtrToStructure(buffer, typeof(T));
                return ObjStrcut != null ? (T)ObjStrcut : default(T);
            }
            catch (Exception) { return default(T); }
            finally { Marshal.FreeHGlobal(buffer); }
        }
    }
}
